Skip to content

Release v0.7.0 into main#347

Merged
ajianaz merged 20 commits into
mainfrom
develop
Jul 16, 2026
Merged

Release v0.7.0 into main#347
ajianaz merged 20 commits into
mainfrom
develop

Conversation

@ajianaz

@ajianaz ajianaz commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Release v0.7.0 → main

Merges develop into main for the v0.7.0 release. A v0.7.0 tag will be pushed to the resulting main commit to trigger the publish workflow (cross-platform binaries + GitHub Release + crates.io publish).

What's in v0.7.0 (20 commits since v0.6.2)

Highlights

Why minor (0.7.0)

New feature (caller context) + deny_unknown_fields is a breaking-minor change for configs relying on silently-ignored typos.

See CHANGELOG.md ## [0.7.0] for the full breakdown.

All CI green on develop; 659 tests pass; clippy/fmt/audit clean.

ajianaz and others added 20 commits June 27, 2026 13:00
…#325)

* fix(review): add post-LLM filter for hardcoded secret false positives

Cora LLM sometimes flags struct field declarations (e.g. api_key: String)
as 'Hardcoded password or secret in variable' even when no literal value
is present. The built-in sec-hardcoded-secret regex only matches actual
assignments like api_key = "sk-...", but the LLM can hallucinate these
findings regardless of .cora.yaml rules.

Add apply_llm_secret_fp_filter that cross-validates LLM security findings
against actual diff lines. If a finding about hardcoded secrets points to
a line that doesn't match the sec-hardcoded-secret regex, it's removed
as a false positive. Unknown/unresolvable lines are kept (safe default).

4 unit tests cover: FP removal, real secret retention, non-security
pass-through, and unknown-line preservation.

* fix(deps): bump git2 0.20 → 0.21 to resolve RUSTSEC advisories

- git2 0.21.0 fixes RUSTSEC-2026-0183 (null ptr in Remote::list)
  and RUSTSEC-2026-0184 (null ptr in Blame::blame_buffer Signature)
- Remote::url() API changed from Option<&str> to Result<&str, Error>
- Adapted get_repo_info() to use .url().ok() chain

* fix(deps): bump quinn-proto 0.11.14 → 0.11.15 (RUSTSEC-2026-0185)

Resolves remote memory exhaustion DoS vulnerability in quinn-proto
unbounded out-of-order stream reassembly.

---------

Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…ging (#328)

- Tighten injection/exec regex: only flag subprocess.run when dynamic
  input signals present (f-string, format(), shell=True, string concat
  with user input). Literal list calls like subprocess.run(cmd) are safe.
- Add debug logging to apply_ignore_rules for better diagnostics
- Add unit tests for ignore_rules (title match, issue_type match, empty,
  case insensitive)
- Add unit tests for security_scanner (no FP on literal list, triggers
  on f-string and shell=True)

Fixes #326

Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…cy and new usage field names

GPT-5.4 (and potentially other newer models) return both legacy
(prompt_tokens/completion_tokens) and new (input_tokens/output_tokens)
field names in the usage object simultaneously.

serde_json >= 1.0.120 tracks visited fields during struct
deserialization. When an alias maps to a field already set from the
primary key, it raises 'duplicate field' error, breaking all reviews
using affected models.

Fix: Replace serde alias-based Usage deserialization with manual
parse_usage_value() that extracts from serde_json::Value with explicit
preference order (primary > camelCase > new). Updated both streaming
and non-streaming code paths.

Closes #330
fix: handle serde duplicate field error when providers send both legacy and new usage field names
- Fix #32: Command injection via  in commit_cmd.rs
  Replace sh -c shell execution with direct Command::new(program).args(args)
  to prevent shell injection through EDITOR env variable.

- Fix #61: Path traversal in JS/TS import resolver
  Add safe_join() helper that canonicalizes paths and verifies containment
  within project root. Apply to JS/TS import resolution and all other
  project_root.join() calls involving user-controlled paths.

- Fix #62: Path traversal in symbol resolver
  Use safe_join() in resolve_symbols, test file resolution, and
  read_entry_content to prevent reading files outside project root.

- Fix #91: API key file written before chmod (TOCTOU race)
  Create file with 0o600 permissions from the start using OpenOptionsExt
  on Unix, eliminating the window between write and chmod.

Closes #332
fix: resolve 4 critical security vulnerabilities (P0)
Fixes:
- #42 Severity sort inverted: sort descending (Critical first) so max_findings truncation drops least important findings
- #45 Security findings dropped on LLM failure: include security_findings in fallback condition and summary
- #52 HashMap nondeterministic hash: sort HashMap entries by key before hashing for deterministic filenames
- #53 Debt score trend wrong: compute recent_avg_quality from recent slice instead of overall average
- #54 Category trend change inflated: build recent_category_counts from recent slice for accurate delta
- #92 Config precedence inverted: env vars > config > auto-detect > defaults
- #93 context_chain config ignored: copy context_chain from ReviewSection in merge_into
- #96 Hook install overwrites existing hooks: use sentinel marker, backup non-cora hooks, compose wrapper

Refs #333
fix: resolve silent data corruption bugs (P1)
fix: update project-sync workflow — add PR trigger, continue-on-error, robust helpers
)

LLM API providers use different parameter names for max output tokens:
- OpenAI (legacy): max_tokens
- OpenAI (new): max_completion_tokens
- Anthropic: max_tokens
- Google Gemini/Vertex: max_output_tokens

cora-cli previously hardcoded 'max_tokens' everywhere, causing HTTP 400 errors
('Unsupported parameter: max_output_tokens') when providers don't accept it.

Changes:
- Add max_tokens_param to LlmSection config schema (supports 'auto' for
  provider-based detection, or explicit override)
- Add resolve_max_tokens_param() in loader.rs for auto-detection logic
- Replace hardcoded 'max_tokens' JSON key with dynamic key in both
  streaming and non-streaming LLM request paths
- Add max_tokens_param to LLMConfig struct for runtime use
- Display max_tokens_param in 'cora config' output

Auto-detection maps:
- gemini/google/vertex -> max_output_tokens
- All other providers -> max_tokens

Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
Co-authored-by: Hermes Agent <hermes@nousresearch.com>
* perf: resolve scan/review pipeline performance bottlenecks (P2)

Fix 10 performance issues — all 'repeated work in loops' patterns:

#41 - Pre-compile regex patterns once per rule instead of per line
#5  - Batch DB queries in handle_find_affected_tests (single IN clause)
#13 - Batch symbol fetch for Affected command (single query)
#14 - Prepare SQL statement once outside test-pattern loop
#59 - Early cutoff in secrets scanner when max_findings reached
#70 - Break out of all loops when symbol cap hit per file
#67 - Cache file reads during sibling search in resolver
#3  - Reuse single Tokio runtime for MCP tool calls (static LazyLock)
#22 - Static LazyLock regexes in detect_function_entry
#24 - Single transaction for prune_deleted instead of per-file

Refs #335

* fix(ci): resolve clippy + security audit failures

- clippy: remove redundant borrow in format! arg (llm.rs:456 &user_prompt)
- security: bump anyhow 1.0.102 -> 1.0.103 (RUSTSEC-2026-0190, downcast_mut unsoundness)
- security: bump crossbeam-epoch 0.9.18 -> 0.9.20 (RUSTSEC-2026-0204, fmt::Pointer null deref)

Refs #335

---------

Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
…eyword (#341)

A merged PR that references issues via "Refs #N" (not "Closes #N") has no
closingIssuesReferences — a legitimate non-error outcome. Previously the
script could abort with exit 1 under 'set -e' when get_item_id() hit an
unexpected GraphQL response shape (e.g. node: null), surfacing as a red
'sync' check on otherwise-green PRs (e.g. PR #338 merge).

- Make get_item_id() and the LINKED query python defensive against null/unexpected shapes
- Guard the empty-LINKED branch call sites with '|| true' so a missing board item never aborts the job
…e) (#342)

Resolves the P3-core items from #334 — config values and profile values
were accepted without semantic checks, so typos and out-of-range values
propagated silently to runtime.

#94 — Config::validate() at load time:
  - temperature 0.0..=2.0, max_tokens/timeout >= 1
  - max_tokens_param, response_format, output.format, hook.mode/on_violation/min_severity
  - provider.base_url must have an http(s)/ws/unix scheme
  - multiple errors aggregated into one message

#81 — Profile::validate(): focus weight 1-10, recognized action/tone/detail_level

#57 — CategoryAction enum (case-insensitive deserialize): unknown values
  like 'blok' now fail loudly instead of silently becoming blocking

#58 — evaluate() forces Pass when enabled=false (disabled gate never fails)

#80 — deny_unknown_fields on all config sections: misspelled YAML keys
  (e.g. 'quailty_gate', 'temprature') rejected at parse time

24 new tests covering validation acceptance/rejection and deny_unknown_fields.
All 637 tests pass; clippy + fmt clean.

Refs #334
…) (#343)

Markdown files (.md/.mdx/.markdown) frequently contain fenced code blocks
that are documentation examples, not executable code. Treating their
contents like real source code produced false positives — e.g. a `git push`
inside a fenced bash block flagged as SQL injection (#329).

New module `engine::markdown` detects fenced code blocks (triple-backtick /
triple-tilde) within a diff chunk and exposes which new-file line numbers
fall inside them. A new filter `apply_markdown_code_block_filter()` drops
findings (from all sources: security/secrets/rules scanners + LLM) whose
file:line lands inside a code block of a markdown file.

Fence state is tracked across full hunk context (Add + Context lines,
skipping Removed lines) so it works even when only the body of a code block
was edited — the opening fence does not need to be part of the diff.

Findings without a resolvable line number are kept (safe default).

11 new tests (7 unit for fence detection + 4 integration for the filter,
including the exact #329 git-push/SQL-injection scenario). All 648 tests
pass; clippy + fmt + audit clean.

Refs #329
Closes the remaining minor items tracked under #334 so the issue can be
fully resolved. Each fix is small and targeted.

- #87 is_test_file: path-segment awareness so `latest`, `aspect`,
  `attestation`, `protest` are not mistaken for test files.
- #66 glob_matches: directory excludes (`src/`) match only at segment
  boundaries (`mysrc/`, `docs/src-guide/` no longer caught).
- #68 estimate_tokens: non-empty content returns at least 1 token (was 0
  under integer division).
- #72 RE_JAVA_IMPORT: allow wildcard capture (`import com.example.*` keeps
  the `*`), plus `import static`.
- #73 RE_RUST_MOD: extract `mod foo;` declarations as dependency symbols,
  matching the documented behavior.
- #23 index_stats: query `PRAGMA page_size` instead of assuming 4096 bytes.
- #48 ReviewIssue.issue_type: serialize as `issue_type` (consistent with
  the field name); keep `type` as a deserialize alias.
- #10 Severity::from_str_lossy: use `eq_ignore_ascii_case` (no allocation).

Already-resolved items verified (no change needed):
- #88 max_findings cutoff already sorts severity-descending before truncate.
- #30 debt snapshot save already emits a warn! on write failure.

8 new tests. All 652 tests pass; clippy + fmt + audit clean.

Closes #334
…ture slicing (#345)

Review previously only resolved OUTBOUND dependencies (what changed code
calls/imports). This adds the INBOUND axis — who calls the changed code
(blast radius) — which is the highest-value context for flagging breaking
signature/type changes, while staying token-economical.

Bug fix (G2): review.rs passed `ignore.rules` (finding-type strings) to the
context resolver instead of `ignore.files` (globs like target/**,
node_modules/**). The resolver could inject build-artifact code, wasting
tokens and adding noise. Now uses ignore.files.

Changes
- G2: review.rs build_context_chain now receives &config.ignore.files.
- Config: new ContextConfig.include_callers (default true);
  max_context_tokens default 3000 -> 5000.
- extraction.rs: extract_definitions_from_diff() — detects functions/types
  DECLARED in added lines for Rust/Python/JS-TS/Go/Java-Kotlin (per-language
  regexes).
- resolver.rs: resolve_callers() walks source files (gitignore-aware via the
  `ignore` crate, so build artifacts are never scanned), bounded by
  MAX_CALLER_FILES_SCAN (400) and MAX_CALLERS_PER_SYMBOL (3). Caller slices
  are tiny (call line + 1 line of context, MAX_CALLER_LINES=4).
- resolver.rs: signature_only() budget fallback — when the full body won't
  fit the remaining budget, inject the signature (up to `{` / 4 lines)
  instead of skipping the entry entirely. New ContextPriority::CallerSite.

Design notes
- follow_depth (outbound recursion) remains at its default of 1; recursive
  outbound resolution was previously a no-op placeholder and properly
  implementing it (cycle-safe, budget-bounded) is a separate larger task.
  The new include_callers axis delivers the real "deeper review" value,
  token-economically.
- Caller resolution never self-reports (defining file excluded), respects
  include_tests, and skips ignored paths.

Tests: 7 new (definition extraction across languages + dedup, caller
resolution finds/skips, signature_only header-only). All 659 tests pass;
clippy + fmt + audit clean.
Consolidates the [Unreleased] entries accumulated since v0.6.2 into a
versioned [0.7.0] release section and updates related documentation.

- Cargo.toml / Cargo.lock: 0.6.2 -> 0.7.0 (minor bump: new caller-context
  feature + deny_unknown_fields is a breaking-minor change for configs
  that previously relied on silently-ignored typos).
- CHANGELOG.md: reorganized into Highlights + Added/Changed/Fixed with
  the v0.7.0 - 2026-07-16 header; fresh empty [Unreleased] on top.
- docs/changelog.md: matching concise release notes.
- docs/configuration.md:
  - new "Cross-File Review Context" section documenting context_chain
    (enabled, max_context_tokens=5000, follow_depth, include_tests,
    include_callers) + outbound/inbound axes + signature fallback.
  - "Config Resolution Order": note that values are validated at load
    and typos rejected via deny_unknown_fields.
  - Quality Gate: note CategoryAction is a case-insensitive validated
    enum and a disabled gate never fails.

No behavior change (docs + version only). All 659 tests pass; clippy +
fmt clean.
@ajianaz
ajianaz merged commit 80fc49b into main Jul 16, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant